package com.cgq.service.impl;

import com.alibaba.fastjson.JSON;
import com.cgq.mapper.CourseMapper;
import com.cgq.pojo.Course;
import com.cgq.service.CourseService;
import io.netty.util.Timeout;
import org.omg.CORBA.TIMEOUT;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * *  Author:陈国强
 * *  Description:
 * *  Date:2023/2/13 22:38
 */
@Service
public class CourseServiceImpl implements CourseService {


    @Autowired
    private CourseMapper mapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;


    //普通查询，不走redis
    @Override
    public List<Course> findCourse() {
        return mapper.selectCourse();
    }

    //利用redis进行缓存查询
    @Override
    public List<Course> findRedisCourse() {
        //1.先从redis进行查询，查看redis缓存中是否有数据
        List<Course> courses = (List<Course>) redisTemplate.opsForValue().get("courseList");
        if (courses != null){
            //2.查询出的course不为空，说明缓存命中，那么直接返回对象即可
            System.err.println("缓存命中，无需再进行数据库的查询");
            return courses;
        }else{
            //3.缓存没有命中，则需要先从数据库中进行数据的查询，则
            System.err.println("缓存未命中，需再进行数据库的查询");
            List<Course> courses1 = mapper.selectCourse();
            //4.将从数据库查出来的对象信息存到redis中，方便下次进行查询
            redisTemplate.opsForValue().set("courseList",courses1);
            return courses1;
        }
    }


    //利用StringRedis进行缓存查询
    @Override
    public List<Course> findStringRedisCourse() {
        //1.先从StringRedis进行查询，查看redis缓存中是否有数据
        String courseList = stringRedisTemplate.opsForValue().get("courseList");
        if (courseList != null){
            //2.查询出的course不为空，说明缓存命中，那么直接返回对象即可
            System.err.println("缓存命中，无需再进行数据库的查询");
            //利用fastjson中的parseArray将这个courseListjson字符串中的数据
            //将json字符串转换成一个一个的实体类（因为json字符串中有很多数据，
            // 因此需要parseArray解析成数组），存入到list集合中去
            List<Course> courses = JSON.parseArray(courseList, Course.class);

            return courses;
        }else {
            //3.缓存没有命中，则需要先从数据库中进行数据的查询，则
            System.err.println("缓存未命中，需再进行数据库的查询");

            List<Course> courses = mapper.selectCourse();
            // JSON.toJSONString将对象转换为json字符串
            stringRedisTemplate.opsForValue().set("courseList", JSON.toJSONString(courses));

            return courses;
        }
    }

    //演示缓存击穿加锁问题
    @Override
    public List<Course> findStringRedisCourse1() {
        //1.先从StringRedis进行查询，查看redis缓存中是否有数据
        String courseList = stringRedisTemplate.opsForValue().get("courseList");
        if (courseList != null){
            //2.查询出的course不为空，说明缓存命中，那么直接返回对象即可
            System.err.println("缓存命中，无需再进行数据库的查询");
            //利用fastjson中的parseArray将这个courseListjson字符串中的数据
            //将json字符串转换成一个一个的实体类（因为json字符串中有很多数据，
            // 因此需要parseArray解析成数组），存入到list集合中去
            List<Course> courses = JSON.parseArray(courseList, Course.class);

            return courses;
        }else{
            //进行加锁,参数为this的原因：单例模式，因为这把锁只在当前的类中有用
            synchronized (this){
                //进入锁之后，还需要再进行redis的缓存查询，原因：线程都在排队进入
                //如果不进行查询redis中的数据，那么依旧是穿透数据库
                System.err.println("现在已经进入锁中");
                courseList = stringRedisTemplate.opsForValue().get("courseList");
                if (courseList != null){
                    //缓存命中
                    List<Course> courses = JSON.parseArray(courseList, Course.class);
                    return courses;
                }


                //加个睡眠
                try {
                    TimeUnit.SECONDS.sleep(5);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }


                System.out.println("缓存未命中，仍需要查询数据库");
                //缓存未命中，仍需要查询数据库
                List<Course> courses = mapper.selectCourse();
                // JSON.toJSONString将对象转换为json字符串，将查询后的结果存储到redis缓存中
                stringRedisTemplate.opsForValue().set("courseList", JSON.toJSONString(courses));
                return courses;
            }
        }
    }

    //演示redis中的setnx中的加锁问题
    @Override
    public List<Course> findStringRedisCourse2() {
        //1.先从StringRedis进行查询，查看redis缓存中是否有数据
        String courseList = stringRedisTemplate.opsForValue().get("courseList");
        if (courseList != null){
            //2.查询出的course不为空，说明缓存命中，那么直接返回对象即可
            System.err.println("缓存命中，无需再进行数据库的查询");
            //利用fastjson中的parseArray将这个courseListjson字符串中的数据
            //将json字符串转换成一个一个的实体类（因为json字符串中有很多数据，
            // 因此需要parseArray解析成数组），存入到list集合中去
            List<Course> courses = JSON.parseArray(courseList, Course.class);

            return courses;
        }else{
            //此时进行加锁用redis中的setnx方法进行加锁
            //也就是redis缓存中没有则添加进去
            Boolean lock = stringRedisTemplate.opsForValue().setIfAbsent("lock", "789");
            //如果lock为true，则说明redis中没有数据的缓存，则需要进行数据库的查询
//            if (lock){
//                //从数据库中查到的值
//                List<Course> courses = mapper.selectCourse();
//                //将其查出来的结果存放在redis缓存中
//                stringRedisTemplate.opsForValue().set("courseList", JSON.toJSONString(courses));
//                return courses;
//            }else {
//                //也就是redis缓存中已经存在，则只进行redis数据的取出
//                courseList = stringRedisTemplate.opsForValue().get("courseList");
//                List<Course> courses = JSON.parseArray(courseList, Course.class);
//                return courses;
//            }

            //由于以上的if-else中，有些线程并不是走在if之后，所以这种情况需要避免，
            //也就是说，若不满足的时候，需要进行自旋

            //占坑失败，进入while自旋
            while(!lock){
                //redis缓存中已经存在，不再查询数据库，直接从redis中返回数据
                courseList = stringRedisTemplate.opsForValue().get("courseList");
                //需要判断从redis缓存中取出来的值是否为空，不为空才行
                if (courseList != null){
                    List<Course> courses = JSON.parseArray(courseList, Course.class);
                    return courses;
                }
            }
        }
        //占坑成功
        System.out.println("缓存没有命中，需要查询数据库");
        //从数据库中查到的值
                List<Course> courses = mapper.selectCourse();
                //将其查出来的结果存放在redis缓存中
                stringRedisTemplate.opsForValue().set("courseList", JSON.toJSONString(courses));
                return courses;
    }
}
